home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Tech Arsenal 1
/
Tech Arsenal (Arsenal Computer).ISO
/
tek-01
/
addpcx.zip
/
IBMTIMER.C
< prev
next >
Wrap
C/C++ Source or Header
|
1988-06-21
|
5KB
|
204 lines
/* IBMTIMER - stopwatch and timer functions for the IBMPC.
This stuff is so system specific that there's no point
in calling it directly from an application.
*/
#include <ibmtimer.h>
/* IBMPC_STOPWATCH
This function implements a simple and very accurate stopwatch.
NOTE that this function must be called at least once per half
hour in order for it to remain accurate. In fact, it will spin
in an almost infinite loop if called less than once per half
hour. Also, for long duration events (greater than a minute,)
it is much better to compute elapsed time using the system
clock (as apposed to the DOS time function, which is based on
the system tick counter and is not accurate at all.
The information required by the stopwatch for each event being
timed is kept in an IBM_STOPWATCH struct, the structure for
which is declared in "ibmtimer.h"
The first parameter is a reset flag. The second is a pointer
to the stopwatch struct for the event being timed. If the
reset flag is true, the stopwatch struct is reset to zero,
and timing begins. If the reset flag is zero, then the
data in the stopwatch struct is updated, and the elapsed time
from the reset operation to the current operation is returned.
The return value is always the number of COUNTER increments
since the last reset operation. ie: 1 second = 1193180 counter
increments.
Operation is as follows:
The 8253 Timer channel 0 is configured as a down counter which
counts 65536 transitions on its clock line, then generates an
interrupt. The interrupt is handled by a routine called TIMER_INT
(see page A-79 of the Tech. Ref. PC-XT.) This routine increments
an int var called TIMER_LOW. Since the input clock to timer channel
is running at 1193180 Hz, TIMER_LOW is incremented at the rate of
1193180 / 2^16 = 18.20648193 times per second. At that rate, it
rolls over once every 2^16 / 18.2 = 3599.597124 seconds, or approx.
once per hour. The error is 111.91us per second on a system
with a perfectly adjusted crystal (very rare.)
This stopwatch works by computing the elapsed time
since the reset of the stopwatch struct using the TIMER_LOW
var and the 8253 channel 0 counter.
Note that due to the problem of reading the DOS time & the
timer channel count while same are being updated, it is
possible that a read error of several microseconds may occur.
To prevent such invalid values from being returned by this
routine, a check is made for samples that are more than 30
minutes apart (that's what the errors look like.) In the
event that such a sample is detected, another sample is
taken. As such, this routine could go into a dead spin for a half
hour if it is not called more than once every half hour.
*/
unsigned long ibmpc_stopwatch( resflg, p )
struct IBMPC_STOPWATCH *p;
int resflg; /* reset flag */
{
static unsigned long read_ibmpc_time();
unsigned long c;
rderr: c = read_ibmpc_time();
if ( resflg )
{ p->last_count = c;
p->elapsed_time = 0l;
};
if ( (c - p->last_count) & 0x80000000l )
goto rderr;
p->elapsed_time += (c - p->last_count);
p->last_count = c;
return p->elapsed_time;
}
/* READ_IBMPC_TIME
Returns the current value of TIMER_LOW as the high order 16 bits
of its result, and the current count in the 8253 timer channel 0
as the low order 16 bits.
*/
unsigned long read_ibmpc_time()
{
#ifndef _lint
#asm
timer equ 040h
;
;routine to return TIMER_LOW:Channel Count
;
push ds ;save current segment
mov ax, 040h ;use segment at 40h
mov ds, ax
mov bx, 06ch ;get offset to TIMER_LOW
mov al, 0 ;ready to read timer
out timer+3,al ;this latches the current count
cli ;disable interrupts
mov dx, [bx] ;get TIMER_LOW
in al, timer+0 ;read the current count
mov ah, al
in al, timer+0
sti ;re-enable interrupts
xchg al, ah
mov cx, ax ;save it
xor ax, ax ;clear ax
sub ax, cx ;convert to positive count
pop ds ;restore DS
#endasm
#else
unsigned long happy_lint;
happy_lint = 123l;
return happy_lint;
#endif
}
/* READ_IBMPC_TIMER
This function simply reads the current value in
the 8253 channel 0.
*/
unsigned int read_ibmpc_timer()
{
#ifndef _lint
#asm
mov al, 0h ;set up to read low/high count
out timer+3,al
in al, timer+0 ;read the current count
mov ah, al
in al, timer+0
xchg al, ah
mov cx, ax ;save it
xor ax, ax ;clear ax
sub ax, cx ;convert to positive count
#endasm
#else
unsigned int happy_lint;
happy_lint = 123;
return happy_lint;
#endif
}
/* CVT_IBMPC_TIME
This function converts any of the time values above
into Minutes, Seconds & Thousandths of seconds.
It returns as its value the time passed to it.
*/
unsigned long cvt_ibmpc_time( time, pm, ps, phs )
unsigned long time;
unsigned int *pm, *ps, *phs;
{
unsigned long t, m, s, hs;
static unsigned long timerf = IBMPC_TIMER0_FREQ;
s = time / timerf; /* total number of seconds */
t = time - (s * timerf);
m = s / 60l; /* number of minutes */
s -= m * 60l; /* remainder is s */
hs = (t * 1000l) / timerf;
*pm = (unsigned int) m;
*ps = (unsigned int) s;
*phs= (unsigned int) hs;
return time;
}